package com.eian.boot.common.core.excel;

import cn.idev.excel.ExcelWriter;
import cn.idev.excel.FastExcel;
import cn.idev.excel.util.MapUtils;
import cn.idev.excel.write.metadata.WriteSheet;
import com.eian.boot.common.core.excel.listener.BaseExcelListener;
import com.eian.boot.common.core.excel.listener.DefaultExcelListener;
import com.eian.boot.common.core.exception.SystemException;
import com.google.common.collect.Lists;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * @author alex.meng
 * @createDate 2025-09-24 21:31
 * @description Excel操作辅助类
 **/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ExcelHelper {
    //==================== 读取Excel ====================

    /**
     * @author alex.meng
     * @description 从InputStream读取Excel
     */
    public static <T> List<T> read(InputStream inputStream, Class<T> head, BaseExcelListener<T> readListener) {
        FastExcel.read(inputStream, head, readListener).doReadAll();
        return readListener.getExcelData();
    }

    /**
     * @author alex.meng
     * @description 从MultipartFile读取Excel
     */
    public static <T> List<T> read(MultipartFile file, Class<T> head, BaseExcelListener<T> readListener) {
        try (InputStream inputStream = file.getInputStream()) {
            return read(inputStream, head, readListener);
        } catch (IOException e) {
            throw new SystemException("文件读取失败");
        }
    }

    /**
     * @author alex.meng
     * @description 从InputStream读取Excel，且使用默认的监听器
     **/
    public static <T> List<T> read(InputStream inputStream, Class<T> head) {
        return read(inputStream, head, new DefaultExcelListener<>());
    }

    /**
     * @author alex.meng
     * @description 从MultipartFile读取Excel 且使用默认的监听器
     */
    public static <T> List<T> read(MultipartFile file, Class<T> head) {
        try (InputStream inputStream = file.getInputStream()) {
            return read(inputStream, head, new DefaultExcelListener<>());
        } catch (IOException e) {
            throw new SystemException("文件读取失败");
        }
    }

    /**
     * @author alex.meng
     * @description 读取本地Excel文件
     **/
    public static <T> List<T> read(String path, Class<T> head, BaseExcelListener<T> readListener) {
        FastExcel.read(path, head, readListener).doReadAll();
        return readListener.getExcelData();
    }

    /**
     * @author alex.meng
     * @description 读取本地Excel文件
     **/
    public static <T> List<T> read(String path, Class<T> head) {
        return read(path, head, new DefaultExcelListener<>());
    }

    //==================== 写入Excel ====================

    /**
     * @author mengjiao
     * @description 分Sheet写入Excel文件
     */
    private static <T> void partitionWrite(List<T> data, int sharding, ExcelWriter writer) {
        List<List<T>> partition = Lists.partition(data, sharding);
        int sheetIndex = 0;
        for (List<T> tList : partition) {
            WriteSheet sheet = FastExcel.writerSheet(sheetIndex++).build();
            writer.write(tList, sheet);
        }
    }

    /**
     * @author alex.meng
     * @description 写入Excel文件
     */
    public static <T> void write(String fileName, Class<T> head, List<T> data) {
        FastExcel.write(fileName, head).sheet().doWrite(data);
    }

    /**
     * @author alex.meng
     * @description 写入Excel文件
     */
    public static <T> void write(String fileName, String sheetName, Class<T> head, List<T> data) {
        FastExcel.write(fileName, head).sheet(sheetName).doWrite(data);
    }

    /**
     * @author alex.meng
     * @description 分Sheet写入Excel文件
     */
    public static <T> void write(String fileName, Class<T> head, List<T> data, int sharding) {
        try (ExcelWriter writer = FastExcel.write(fileName, head).build()) {
            partitionWrite(data, sharding, writer);
        }
    }


    /**
     * @author alex.meng
     * @description 不同数据类型分Sheet写入Excel文件
     */
    public static void write(String fileName, Map<Class<?>, List<?>> sheetData) {
        try (ExcelWriter writer = FastExcel.write(fileName).build()) {
            int sheetIndex = 0;
            for (Map.Entry<Class<?>, List<?>> entry : sheetData.entrySet()) {
                WriteSheet sheet = FastExcel.writerSheet(sheetIndex++).build();
                writer.write(entry.getValue(), sheet);
            }
        }
    }

    /**
     * @author alex.meng
     * @description 配置web响应头，用于文件下载
     */
    private static void webHeaderConfig(HttpServletResponse response, String fileName) {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String finalFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + finalFileName + ".xlsx");
    }

    /**
     * @author alex.meng
     * @description web下载失败响应
     */

    @SneakyThrows
    private static void webExcelFailResponse(HttpServletResponse response, String errorMsg) {
        response.reset();
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        Map<String, String> map = MapUtils.newHashMap();
        map.put("status", "failure");
        map.put("message", "下载文件失败" + errorMsg);
        response.getWriter().println(map);
    }

    /**
     * @author alex.meng
     * @description web下载
     */
    public static <T> void write(HttpServletResponse response, String fileName, Class<T> head, List<T> data) {
        try {
            webHeaderConfig(response, fileName);
            FastExcel.write(response.getOutputStream(), head).sheet().doWrite(data);
        } catch (Exception e) {
            webExcelFailResponse(response, e.getMessage());
        }
    }

    /**
     * @author alex.meng
     * @description web下载，同类型数据分Sheet写入
     */
    public static <T> void write(HttpServletResponse response, String fileName, Class<T> head, List<T> data, int sharding) {
        try (ExcelWriter writer = FastExcel.write(response.getOutputStream(), head).build()) {
            webHeaderConfig(response, fileName);
            partitionWrite(data, sharding, writer);
        } catch (Exception e) {
            webExcelFailResponse(response, e.getMessage());
        }
    }

    /**
     * @author alex.meng
     * @description web下载，不同数据类型分Sheet写入
     */
    public static void write(HttpServletResponse response, String fileName, Map<Class<?>, List<?>> sheetData) {
        try (ExcelWriter writer = FastExcel.write(response.getOutputStream()).build()) {
            webHeaderConfig(response, fileName);
            int sheetIndex = 0;
            for (Map.Entry<Class<?>, List<?>> entry : sheetData.entrySet()) {
                WriteSheet sheet = FastExcel.writerSheet(sheetIndex++).build();
                writer.write(entry.getValue(), sheet);
            }
        } catch (Exception e) {
            webExcelFailResponse(response, e.getMessage());
        }
    }
}
